package com.qk.utils;

import com.aliyun.oss.ClientBuilderConfiguration;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.model.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.io.ByteArrayInputStream;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class AliyunOSSOperators {

    @Autowired
    private AliyunOSSProperties aliyunOSSProperties;

    // 配置常量
    private static final int DEFAULT_BATCH_SIZE = 100;
    private static final int DEFAULT_MAX_KEYS = 1000;
    private static final long DEFAULT_DELAY_MS = 100L;

    /**
     * 上传文件到OSS
     */
    public String upload(byte[] content, String objectName) {
        validateObjectName(objectName);

        String endpoint = aliyunOSSProperties.getEndpoint();
        String bucketName = aliyunOSSProperties.getBucketName();

        OSS ossClient = createOSSClient();
        try {
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName,
                    new ByteArrayInputStream(content));

            ossClient.putObject(putObjectRequest);

            return buildFileUrl(bucketName, endpoint, objectName);
        } finally {
            shutdownOSSClient(ossClient);
        }
    }

    /**
     * 检查文件是否存在
     */
    public boolean doesObjectExist(String fileKey) {
        if (!StringUtils.hasText(fileKey)) {
            return false;
        }

        String bucketName = aliyunOSSProperties.getBucketName();
        OSS ossClient = createOSSClient();

        try {
            return ossClient.doesObjectExist(bucketName, fileKey);
        } catch (Exception e) {
            log.error("检查OSS文件是否存在失败: {}, fileKey: {}", e.getMessage(), fileKey, e);
            return false;
        } finally {
            shutdownOSSClient(ossClient);
        }
    }

    /**
     * 获取OSS存储桶中的所有文件key
     */
    public Set<String> getOssFileKeys() {
        return getOssFileKeys(null, null);
    }

    /**
     * 根据最后修改时间获取OSS文件key
     * @param prefix 文件前缀
     * @param sinceTime 文件最后修改时间
     */
    public Set<String> getOssFileKeys(String prefix, LocalDateTime sinceTime) {
        String bucketName = aliyunOSSProperties.getBucketName();
        OSS ossClient = createOSSClient();
        Set<String> fileKeys = new HashSet<>();

        try {
            String nextMarker = null;
            ObjectListing objectListing;
            // 计算时间阈值
            Date sinceDate = sinceTime != null ? java.sql.Timestamp.valueOf(sinceTime) : null;

            do {
                ListObjectsRequest listObjectsRequest = new ListObjectsRequest(bucketName)
                        .withMarker(nextMarker)
                        .withMaxKeys(DEFAULT_MAX_KEYS);

                if (StringUtils.hasText(prefix)) {
                    listObjectsRequest.withPrefix(prefix);
                }

                objectListing = ossClient.listObjects(listObjectsRequest);

                // 过滤文件
                for (OSSObjectSummary objectSummary : objectListing.getObjectSummaries()) {
                    // 如果没有时间限制，或者文件最后修改时间在指定时间之后
                    if (sinceDate == null ||
                            !objectSummary.getLastModified().before(sinceDate)) {
                        fileKeys.add(objectSummary.getKey());
                    }
                }

                nextMarker = objectListing.getNextMarker();
            } while (objectListing.isTruncated());

            log.info("从OSS存储桶 {} 中获取到 {} 个文件{}", bucketName, fileKeys.size(), sinceTime != null ? "（基于时间过滤）" : "");

        } catch (Exception e) {
            log.error("获取OSS文件列表失败", e);
            throw new RuntimeException("获取OSS文件列表失败: " + e.getMessage(), e);
        } finally {
            shutdownOSSClient(ossClient);
        }
        return fileKeys;
    }

    /**
     * 批量删除OSS文件
     */
    public int deleteFiles(Set<String> fileKeys) {
        return deleteFiles(fileKeys, DEFAULT_BATCH_SIZE);
    }

    /**
     * 批量删除OSS文件（可指定批次大小）
     */
    public int deleteFiles(Set<String> fileKeys, int batchSize) {
        if (fileKeys == null || fileKeys.isEmpty()) {
            log.info("没有需要删除的文件");
            return 0;
        }

        validateBatchSize(batchSize);
        String bucketName = aliyunOSSProperties.getBucketName();
        OSS ossClient = createOSSClient();
        int totalDeleted = 0;

        try {
            List<String> fileKeyList = new ArrayList<>(fileKeys);

            for (int i = 0; i < fileKeyList.size(); i += batchSize) {
                int end = Math.min(i + batchSize, fileKeyList.size());
                List<String> batch = fileKeyList.subList(i, end);

                DeleteObjectsRequest deleteRequest = new DeleteObjectsRequest(bucketName)
                        .withKeys(batch)
                        .withQuiet(false);

                DeleteObjectsResult deleteResult = ossClient.deleteObjects(deleteRequest);
                int deletedInBatch = deleteResult.getDeletedObjects().size();
                totalDeleted += deletedInBatch;

                log.info("批量删除 {} 个文件，成功删除 {} 个", batch.size(), deletedInBatch);

                // 添加延迟避免频繁请求（非最后一批次）
                if (end < fileKeyList.size()) {
                    sleepSafely();
                }
            }

            log.info("OSS文件删除完成，总共删除 {} 个文件", totalDeleted);

        } catch (Exception e) {
            log.error("删除OSS文件失败", e);
            throw new RuntimeException("删除OSS文件失败: " + e.getMessage(), e);
        } finally {
            shutdownOSSClient(ossClient);
        }

        return totalDeleted;
    }

    /**
     * 构建文件访问URL
     */
    private String buildFileUrl(String bucketName, String endpoint, String objectName) {
        // 移除endpoint中的协议头（http:// 或 https://）
        String cleanEndpoint = endpoint.replaceAll("^https?://", "");
        return String.format("https://%s.%s/%s", bucketName, cleanEndpoint, objectName);
    }

    /**
     * 获取OSS文件URL的前缀长度
     * 用于从完整URL中截取文件key
     *
     * @return URL前缀的长度
     */
    public int getUrlPrefixLength() {
        String bucketName = aliyunOSSProperties.getBucketName();
        String endpoint = aliyunOSSProperties.getEndpoint();
        String cleanEndpoint = endpoint.replaceAll("^https?://", "");
        String urlPrefix = String.format("https://%s.%s/", bucketName, cleanEndpoint);
        return urlPrefix.length();
    }

    /**
     * 验证对象名称
     */
    private void validateObjectName(String objectName) {
        if (!StringUtils.hasText(objectName)) {
            throw new IllegalArgumentException("对象名称不能为空");
        }
    }

    /**
     * 验证批次大小
     */
    private void validateBatchSize(int batchSize) {
        if (batchSize <= 0 || batchSize > 1000) {
            throw new IllegalArgumentException("批次大小必须在1-1000之间");
        }
    }

    /**
     * 安全睡眠方法
     */
    private void sleepSafely() {
        try {
            TimeUnit.MILLISECONDS.sleep(AliyunOSSOperators.DEFAULT_DELAY_MS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("线程被中断", e);
        }
    }

    /**
     * 创建OSS客户端
     */
    private OSS createOSSClient() {
        String endpoint = aliyunOSSProperties.getEndpoint();
        String region = aliyunOSSProperties.getRegion();

        ClientBuilderConfiguration config = new ClientBuilderConfiguration();
        config.setSignatureVersion(SignVersion.V4);

        return OSSClientBuilder.create()
                .endpoint(endpoint)
                .credentialsProvider(new EnvironmentVariableCredentialsProvider())
                .clientConfiguration(config)
                .region(region)
                .build();
    }

    /**
     * 关闭OSS客户端
     */
    private void shutdownOSSClient(OSS ossClient) {
        if (ossClient != null) {
            try {
                ossClient.shutdown();
            } catch (Exception e) {
                log.warn("关闭OSS客户端时发生异常", e);
            }
        }
    }
}